/*
 * @Author: 13177709051 3506463075@qq.com
 * @Date: 2024-10-22 21:12:57
 * @LastEditors: 13177709051 3506463075@qq.com
 * @LastEditTime: 2024-10-22 22:39:05
 * @FilePath: /linux20/textPipe/textpipe.cc
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE`
 */
#include <iostream>
#include <string>
#include <cerrno> //errno.h
#include <unctrl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>

// fork之后子进程是能拿到父进程的数据的 --- 写时拷贝

const int size = 1024;

// 获取消息函数
// 功能 : 生成一个包含消息ID 和当前进程ID 的字符串, 作为子进程发送给父进程的消息的一部分.
std::string getOtherMessage()
{
    static int cnt = 0;
    std::string messageid = std::to_string(cnt); // stoi -> string -> int
    cnt++;
    pid_t self_id = getpid();
    std::string stringpid = std::to_string(self_id);

    std::string message = "messageid:";
    message += messageid;
    message += "my pid is : ";
    message += stringpid;

    return message;
}

// 子进程写入模块
//  功能 : 子进程通过管道向父进程写入
void SubProcessWrite(int wfd)
{
    std::string message = "father, I am your son process!";
    while (true)
    {
        std::cerr << "++++++++++++++++++++++++++++++++++++++" << std::endl;
        std::string info = message + getOtherMessage(); // 这条消息, 就是我们子进程发给父进程的消息
        write(wfd, info.c_str(), info.size());          // 写入管道的时候, 没有写入\0, 有没有必要

        std::cerr << info << std::endl;
    }
    std::cout << "child quit ..." << std::endl;
}

// \0 字符: 在 C++ 中，字符串是以 null 字符 \0 结尾的。write() 函数写入的是指定长度的字节（由 info.size() 决定），
// 因此不需要显式写入 \0。当父进程读取这条消息时，读取的字节数是已知的，所以不需要依赖字符串的结尾。

// 1. 区分标准输出和错误输出
// std::cerr 是标准错误流，用于输出错误信息或调试信息，而 std::cout 是标准输出流，用于正常的程序输出。
// 将调试信息或错误信息发送到 std::cerr，可以清晰地将这些信息与正常的程序输出区分开来。这在复杂程序中尤其重要，因为它有助于在审查日志时快速识别问题。
// 2. 输出缓冲机制
// std::cout 通常是带缓冲的输出流，可能会因为缓冲区未满而延迟输出信息。这在某些情况下可能导致调试信息不能立即看到。
// 相比之下，std::cerr 是不带缓冲的，意味着信息会立即被输出。这在调试过程中非常有用，因为你希望能即时看到任何错误或状态信息，而不是等到缓冲区填满。
// 3. 调试和监控
// 在开发和调试过程中，使用 std::cerr 输出调试信息（如进程状态、错误信息等）是个常见做法，可以帮助开发者实时监控程序的运行状态。
// 如果程序崩溃或出现异常，std::cerr 中的输出通常会被保留下来，而 std::cout 的输出可能因为程序的崩溃而丢失。

// 功能: SubProcessWrite 函数的主要目的是通过管道向父进程发送消息。它构造了一条包含固定消息和动态消息的字符串，并通过管道不断地发送。

// 父进程读取模块
//  功能 : 父进程从管道中读取子进程发送的消息
void FatherProcessRead(int rfd)
{
    char inbuffer[size];
    while (true)
    {
        sleep(2);
        std::cout << "------------------------------------" << std::endl;
        size_t n = read(rfd, inbuffer, sizeof(inbuffer) - 1); // sizeof(inbuffer) -> strlen(inbuffer)
        if (n > 0)
        {
            inbuffer[n] = 0; // == '\0'
            std::cout << inbuffer << std::endl;
        }
        else if (n == 0)
        {
            // 如果read返回值是0, 表示写端直接关闭了, 我们读到了文件的结尾
            std::cout << "client quit father get return val: " << n << "father quit too!" << std::endl;
            break;
        }
        else if (n < 0)
        {
            std::cerr << "read error" << std::endl;
            break;
        }
    }
}
// FatherProcessRead 函数的主要目的是从子进程通过管道读取数据并输出到标准输出。它处理三种读取结果：正常读取、读到文件结束和读取错误 

// 主函数
//  功能 : 管理进程的创建和管道的操作
int main()
{
    // 1. 创建管道
    int pipefd[2];
    int n = pipe(pipefd); // 输出型参数, rfd, wfd
    if (n != 0)
    {
        std::cerr << "errno: " << errno << ": " << "errstring : " << strerror(errno) << std::endl;
        return 1;
    }
    //pipefd[0] -> 0 -> r(读) pipefd[1] -> 1 -> w(写)
    std::cout << "pipefd[0]: " << pipefd[0] << ", pipefd[1]: " << pipefd[1] << std::endl;
    sleep(1);

    //2. 创建子进程
    pid_t id = fork();
    if(id == 0)
    {
        std::cout << "子进程关闭不需要的fd了, 准备发消息了" << std::endl;
        sleep(1);
        //子进程 --- write
        //3. 关闭不需要的fd
        close(pipefd[0]);

        SubProcessWrite(pipefd[1]);
        close(pipefd[1]);
        exit(0);
    }

    std::cout << "父进程关闭不需要的fd了, 准备收消息了" << std::endl;
    sleep(1);
    //父进程 --- read
    //3. 关闭不需要的fd
    close(pipefd[1]);
    FatherProcessRead(pipefd[0]);
    std::cout << "5S, father close rfd" << std::endl;
    sleep(5);
    close(pipefd[0]);

    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
    {
        std::cout << "wait child process done, exit sig : " << (status&0x7f) << std::endl;
        std::cout << "wait child process done, exit code(ign) : " << ((status >> 8)& 0xFF) << std::endl; 
    }
    return 0;
}

// pipefd: 这是一个整数数组，pipefd[0] 用于读取数据（读端），pipefd[1] 用于写入数据（写端）。
// pipe(): 创建管道，如果成功则返回 0，并将读端和写端的文件描述符分别存储在 pipefd[0] 和 pipefd[1] 中。

// fork(): 创建一个子进程。返回值 id 为 0 表示当前进程是子进程。


// 调用 SubProcessWrite(pipefd[1])，假设这个函数负责将数据写入管道。
// 写入完成后，关闭写端 pipefd[1]，并通过 exit(0) 结束子进程。


// 父进程关闭管道的写端 pipefd[1]，因为它只需要读取数据。
// 调用 FatherProcessRead(pipefd[0])，假设这个函数负责从管道中读取数据。

// waitpid(): 等待子进程结束并获取其状态。



// 状态处理:
// status & 0x7f 用于获取子进程的退出信号。
// ((status >> 8) & 0xFF) 用于获取子进程的退出码。
// 如果成功，输出子进程的退出信号和退出码。

